<?php
namespace LastDB;

use PDO;
use Exception;

/**
 * super Lightweight database framework
 *
 * 增 insert - LAST_INSERT_ID
 * 删 deled
 * 改 update
 * 查 select - selectone
 *
 * @author YS
 *        
 */
class LastDB
{

    protected static $DB = null;

    protected static $connect = null;

    protected static $lastInsertId = false;

    private static $_transaction = true;

    private $debug = FALSE;

    protected $options = [];

    private function initialization()
    {
        $this->options = [
            'field' => '*',
            'table' => '',
            'where' => '',
            'order' => '',
            'group' => '',
            'join' => '',
            'limit' => '',
            'params' => [],
            'data' => []
        ];
    }

    private function implement_log($errorInfo, $sql = '', $params = null)
    {
        self::$_transaction = false;
        $errno = $errorInfo[1];
        $error = $errorInfo[2];
        
        echo '执行sql错误,错误码:' . $errno . ',异常描述:' . $error . '.异常sql:' . $sql . '.参数:' . json_encode($params, JSON_UNESCAPED_UNICODE);
        echo '<br>';
    }

    public function __construct($config = [])
    {
        self::$connect = $this->pdo_connect();
    }

    private function pdo_connect()
    {
        try {
            
            $db = array(
                'host' => '127.0.0.1',
                'port' => '3306',
                'dbname' => 'test',
                'username' => 'root',
                'password' => '123456',
                'charset' => 'utf8'
            );
            
            $hostanme = $db['host'];
            $dbname = $db['dbname'];
            $username = $db['username'];
            $password = $db['password'];
            $conn = new PDO('mysql:host=' . $hostanme . ';dbname=' . $dbname, $username, $password);
            $conn->exec('set character_set_client = utf8');
            $conn->exec('SET character_set_results =utf8');
            $conn->exec('SET character_set_connection = utf8');
            $conn->setAttribute(PDO::ATTR_ORACLE_NULLS, true);
            return $conn;
        } catch (\Exception $e) {
            $errorInfo[1] = $e->getCode();
            $errorInfo[2] = $e->getMessage();
            $this->implement_log($errorInfo);
            return false;
        }
    }

    /**
     * 验证是否为函数
     *
     * @param string $value            
     */
    private function is_function(&$value)
    {
        $bool = strpos($value, '&');
        if ($bool === false) {
            return false;
        }
        $value = ltrim($value, '&');
        return true;
    }

    /**
     * table name
     *
     * @param string $table            
     * @return \LastDB\LastDB
     */
    public function table($table)
    {
        $this->options['table'] = $table;
        return $this;
    }

    /**
     * 更新数据
     *
     * @param array $data            
     * @return \LastDB\LastDB
     */
    public function set($data)
    {
        $params = [];
        $data_set_value = '';
        
        foreach ($data as $key => $value) {
            $params_key = ':set_' . str_replace('.', '', $key);
            if ($this->is_function($value)) {
                $data_set_value .= $key . ' = ' . $value . ' , ';
            } else {
                $data_set_value .= $key . ' = ' . $params_key . ' , ';
                $params[$params_key] = $value;
            }
        }
        
        $data_set_value = rtrim(trim($data_set_value), ',');
        
        if ($data_set_value) {
            $data_set_value = ' set ' . $data_set_value;
        }
        $this->options['data'] = $data_set_value;
        $this->options['params'] = array_merge($this->options['params'], $params);
        
        return $this;
    }

    /**
     * 插入对象数据
     *
     * @param array $data            
     * @return \LastDB\LastDB
     */
    public function data($data)
    {
        $data_values = '';
        $params = [];
        $field_values = '';
        $i = 0;
        foreach ($data as $data_key => $data_value) {
            if (is_array($data_value)) {
                foreach ($data_value as $data_value_key => $data_value_value) {
                    if ($i == 0) {
                        $field_values .= $data_value_key . ',';
                    }
                    
                    if (! $this->is_function($data_value_value)) {
                        $data_values[$i][] = ':' . $data_value_key . $i;
                        $params_key = ':' . $data_value_key . $i;
                        $params[$params_key] = $data_value_value;
                    } else {
                        $data_values[$i][] = $data_value_value;
                    }
                }
                
                $i ++;
            } else {
                $field_values .= $data_key . ',';
                if (! $this->is_function($data_value)) {
                    $data_values .= ':' . $data_key . ',';
                    $params_key = ':' . $data_key;
                    $params[$params_key] = $data_value;
                } else {
                    $data_values .= $data_value . ',';
                }
            }
        }
        if (is_string($data_values)) {
            $data_values = trim($data_values, ',');
        }
        if (is_string($field_values)) {
            $field_values = trim($field_values, ',');
        }
        
        $this->options['field'] = $field_values;
        $this->options['data'] = $data_values;
        $this->options['params'] = array_merge($this->options['params'], $params);
        return $this;
    }

    /**
     * join table name
     *
     * @param array $join
     *            [
     *            'tablename' => [
     *            'type' => 'left',
     *            'on' => 'tablename.id = Associationtable.id',
     *            'where' => []|string
     *            ]
     *            ]
     * @return \LastDB\LastDB
     */
    public function join($join)
    {
        $join_values = '';
        $params = [];
        if (is_array($join)) {
            foreach ($join as $join_key => $join_value) {
                $join_values .= $join_value['type'] . ' join ' . $join_key . ' on ' . $join_value['on'] . ' ';
                if (isset($join_value['where'])) {
                    if (is_array($join_value['where'])) {
                        $where = $join_value['where'];
                        $params = [];
                        $where_values = '';
                        foreach ($where as $where_key => $where_value) {
                            // 别名.列名时 去掉点
                            $params_key = ':join' . str_replace('.', '', $where_key);
                            if (is_array($where_value)) {
                                if ($this->is_function($where_value[key($where_value)])) {
                                    $where_values .= $where_key . key($where_value) . $where_value[key($where_value)] . ' and ';
                                } else {
                                    $where_values .= $where_key . key($where_value) . $params_key . ' and ';
                                    $params[$params_key] = $where_value[key($where_value)];
                                }
                            } else {
                                if ($this->is_function($where_value)) {
                                    $where_values .= $where_key . key($where_value) . $where_value . ' and ';
                                } else {
                                    $where_values .= $where_key . ' = ' . $params_key . ' and ';
                                    $params[$params_key] = $where_value;
                                }
                            }
                        }
                        
                        $where_values = rtrim(trim($where_values), 'and');
                        
                        if ($where_values) {
                            $join_values .= ' and ' . $where_values;
                        }
                    } else {
                        $join_values .= ' and ' . $join_value['where'];
                    }
                }
            }
        }
        // $order = trim($order, ',');
        $this->options['params'] = array_merge($this->options['params'], $params);
        $this->options['join'] = $join_values;
        return $this;
    }

    /**
     * filed
     *
     * @param string|array $field            
     * @return \LastDB\LastDB
     */
    public function field($field)
    {
        $field_values = '';
        if (is_array($field)) {
            // $field = implode(',', $field);
            foreach ($field as $field_key => $field_value) {
                if (is_array($field_value)) {
                    foreach ($field_value as $field_value_key => $field_value_value) {
                        $field_values .= $field_key . '.' . $field_value_value . ',';
                    }
                } else {
                    $field_values .= $field_value . ',';
                }
            }
        } else {
            $field_values = $field;
        }
        
        $field_values = trim($field_values, ',');
        $this->options['field'] = $field_values;
        return $this;
    }

    /**
     * where
     *
     * @param array $where            
     * @return \LastDB\LastDB
     */
    public function where($where = [])
    {
        if ($where) {
            $params = [];
            $where_values = '';
            foreach ($where as $where_key => $where_value) {
                // 别名.列名时 去掉点
                $params_key = ':' . str_replace('.', '', $where_key);
                if (is_array($where_value)) {
                    if ($this->is_function($where_value[key($where_value)])) {
                        $where_values .= $where_key . key($where_value) . $where_value[key($where_value)] . ' and ';
                    } else {
                        $where_values .= $where_key . key($where_value) . $params_key . ' and ';
                        $params[$params_key] = $where_value[key($where_value)];
                    }
                } else {
                    if ($this->is_function($where_value)) {
                        $where_values .= $where_key . key($where_value) . $where_value . ' and ';
                    } else {
                        $where_values .= $where_key . ' = ' . $params_key . ' and ';
                        $params[$params_key] = $where_value;
                    }
                }
            }
            
            $where_values = rtrim(trim($where_values), 'and');
            
            if ($where_values) {
                $where_values = ' where ' . $where_values;
            }
            
            $this->options['where'] = $where_values;
            $this->options['params'] = array_merge($this->options['params'], $params);
        }
        return $this;
    }

    /**
     * order by
     *
     * @param string|array $order            
     * @return \LastDB\LastDB
     */
    public function order($order = [])
    {
        if ($order) {
            $order_values = "";
            if (is_array($order)) {
                foreach ($order as $order_key => $order_value) {
                    if (is_array($order_value)) {
                        foreach ($order_value as $order_value_key => $order_value_value) {
                            $order_values .= $order_value_key . ' ' . $order_value_value . ',';
                        }
                    } else {
                        $order_values .= $order_key . ' ' . $order_value;
                    }
                }
            } else {
                $order_values = $order;
            }
            $order_values = trim($order_values, ',');
            
            $this->options['order'] = ' order by ' . $order_values;
        }
        return $this;
    }

    /**
     * group by
     *
     * @param string|array $group            
     * @return \LastDB\LastDB
     */
    public function group($group = [])
    {
        if ($group) {
            $group_values = "";
            if (is_array($group)) {
                foreach ($group as $group_key => $group_value) {
                    if (is_array($group_value)) {
                        foreach ($group_value as $group_value_key => $group_value_value) {
                            $group_values .= $group_value_key . ' ' . $group_value_value . ',';
                        }
                    } else {
                        $group_values .= $group_key . ' ' . $group_value;
                    }
                }
            } else {
                $group_values = $group;
            }
            $group_values = trim($group_values, ',');
            
            $this->options['group'] = ' group by ' . $group_values;
        }
        return $this;
    }

    /**
     * limit
     *
     * @param int $limit            
     * @return \LastDB\LastDB
     */
    public function limit($limit)
    {
        if (is_array($limit)) {
            $this->options['limit'] = 'limit ' . $limit[0] . ',' . $limit[1];
        } else {
            $this->options['limit'] = ' limit ' . $limit;
        }
        
        return $this;
    }

    public function getLastInsertId()
    {
        self::$lastInsertId = true;
        return $this;
    }

    /**
     *
     * @return \LastDB\LastDB
     */
    public static function db()
    {
        if (empty(self::$DB)) {
            // self::$DB = new self();
            $calledClassName = get_called_class();
            self::$DB = new $calledClassName();
        }
        self::$DB->initialization();
        return self::$DB;
    }

    /**
     * 查询条数
     */
    public function count()
    {
        $fields = $this->options['field'];
        $this->options['field'] = ' count(0) as cn ';
        $result = $this->selectone();
        $cn = 0;
        if ($result) {
            $cn = $result['cn'];
        }
        $this->options['field'] = $fields;
        return $cn;
    }

    /**
     * 查询一条结果
     *
     * @return mixed|boolean
     */
    public function selectone()
    {
        $structure = $this->structure(1);
        $sql = $structure['sql'];
        $params = $structure['params'];
        try {
            if ($params) {
                $dbEx = self::$connect->prepare($sql);
                $dbEx->execute($params);
            } else {
                $dbEx = self::$connect->query($sql);
            }
            return $dbEx->fetch(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            $errorInfo[1] = $e->getCode();
            $errorInfo[2] = $e->getMessage();
            $this->implement_log($errorInfo, $sql, $params);
            return false;
        }
    }

    /**
     * 查询全部信息
     *
     * @return mixed|boolean
     */
    public function select()
    {
        $structure = $this->structure(1);
        $sql = $structure['sql'];
        $params = $structure['params'];
        
        try {
            if ($params) {
                $dbEx = self::$connect->prepare($sql);
                $dbEx->execute($params);
            } else {
                $dbEx = self::$connect->query($sql);
            }
            return $dbEx->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            $errorInfo[1] = $e->getCode();
            $errorInfo[2] = $e->getMessage();
            $this->implement_log($errorInfo, $sql, $params);
            return false;
        }
    }

    public function insert()
    {
        $structure = $this->structure(2);
        $sql = $structure['sql'];
        $params = $structure['params'];
        try {
            $dbEx = self::$connect->prepare($sql);
            if ($dbEx->execute($params)) {
                if (self::$lastInsertId) {
                    return $dbEx->lastInsertId();
                }
                return true;
            } else {
                $this->implement_log($dbEx->errorInfo(), $sql, $params);
                return false;
            }
        } catch (Exception $e) {
            $errorInfo[1] = $e->getCode();
            $errorInfo[2] = $e->getMessage();
            $this->implement_log($errorInfo, $sql, $params);
            return false;
        }
    }

    public function update()
    {
        $structure = $this->structure(3);
        $sql = $structure['sql'];
        $params = $structure['params'];
        try {
            $dbEx = self::$connect->prepare($sql);
            if ($dbEx->execute($params)) {
                return true;
            } else {
                $this->implement_log($dbEx->errorInfo(), $sql, $params);
                return false;
            }
        } catch (Exception $e) {
            $errorInfo[1] = $e->getCode();
            $errorInfo[2] = $e->getMessage();
            $this->implement_log($errorInfo, $sql, $params);
            return false;
        }
    }

    /**
     * 事务开始
     *
     * @return \LastDB\LastDB
     */
    public function transaction()
    {
        self::$connect->beginTransaction();
        return $this;
    }

    /**
     * 提交事务
     */
    public function transaction_commit()
    {
        if (self::$_transaction) {
            return self::$connect->commit();
        } else {
            $this->transaction_rollback();
            return false;
        }
    }

    /**
     * 回滚事务
     */
    public function transaction_rollback()
    {
        return self::$connect->rollBack();
    }

    /**
     * 构造参数为可执行的sql
     *
     * @param int $type
     *            1-查询
     *            2-插入
     *            3-更新
     *            3-查询条数
     */
    private function structure($type)
    {
        $p = ' ';
        $structure['sql'] = '';
        $structure['params'] = '';
        switch ($type) {
            case 1:
                $structure['sql'] = 'select ' . $this->options['field'] . ' from ' . $this->options['table'] . $p . $this->options['join'] . $p . $this->options['where'] . $p . $this->options['group'] . $p . $this->options['order'] . $p . $this->options['limit'];
                $structure['params'] = $this->options['params'];
                break;
            case 2:
                if (is_array($this->options['data'])) {
                    $values = '';
                    foreach ($this->options['data'] as $key => $value) {
                        $values .= '(' . implode(',', $value) . '),';
                    }
                    $values = trim($values, ',');
                    $structure['sql'] = 'insert into ' . $this->options['table'] . ' ( ' . $this->options['field'] . ' ) 
                        VALUES ' . $values;
                } else {
                    $structure['sql'] = 'insert into ' . $this->options['table'] . ' ( ' . $this->options['field'] . ' ) VALUES (' . $this->options['data'] . ')';
                }
                $structure['params'] = $this->options['params'];
                break;
            case 3:
                $structure['sql'] = 'update ' . $this->options['table'] . $this->options['data'] . $p . $this->options['where'] . $p . $this->options['limit'];
                $structure['params'] = $this->options['params'];
                break;
        }
        if ($this->debug) {
            print_r($structure);
        }
        return $structure;
    }

    /**
     * 分页方法
     *
     * @param int $pagesize
     *            每页条数
     * @param int $pageindex
     *            页数
     *            
     * @return array count总条数pagecount总页数date查询结果
     */
    public function paging($pagesize, $pageindex)
    {
        $rtnArray["count"] = 0; // 总条数
        $rtnArray["pagecount"] = 0; // 总页数
        $rtnArray["pageindex"] = $pageindex; // 当前页码
        $rtnArray["pagesize"] = $pagesize; // 每页条数
        $rtnArray["date"] = array(); // 查询结果
        
        $Count = $this->count();
        if ($Count > 0) {
            $pageCount = ceil($Count / $pagesize); // 总页数
            if ($pageindex > $pageCount) {
                // 请求页数大于总页数 直接返回
                $rtnArray["count"] = $Count; // 总条数
                return $rtnArray;
            }
            
            $startLimit = ($pageindex - 1) * $pagesize; // 分页开始条数
            $endLimit = $pagesize; // 分页条数
            $this->limit([
                $startLimit,
                $endLimit
            ]);
            $selectResult = $this->select();
            
            $rtnArray["count"] = $Count; // 总条数
            $rtnArray["pagecount"] = $pageCount; // 总页数
            $rtnArray["date"] = ! empty($selectResult) ? $selectResult : array(); // 查询结果;
            return $rtnArray;
        } else {
            // 没有查询结果 直接返回
            return $rtnArray;
        }
    }
}
